package pl.edu.agh.pros.internal.proxy.service;

import java.util.UUID;

import org.jivesoftware.smack.packet.Message;
import org.ros.exception.RemoteException;
import org.ros.exception.ServiceNotFoundException;
import org.ros.namespace.GraphName;
import org.ros.node.ConnectedNode;
import org.ros.node.Node;
import org.ros.node.service.ServiceClient;
import org.ros.node.service.ServiceResponseListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.agh.pros.conf.ProxyConfiguration;
import pl.edu.agh.pros.internal.proxy.AbstractProxyNode;
import pl.edu.agh.pros.internal.proxy.IMessageReceiver;
import pl.edu.agh.pros.internal.proxy.ProxyDispatcher;
import pl.edu.agh.pros.prop.Props;
import pl.edu.agh.pros.util.RosServiceUtils;

public class ProxyServiceReceiver extends AbstractProxyNode implements IMessageReceiver {

	private static final Logger logger = LoggerFactory.getLogger(ProxyServiceReceiver.class);
	private ServiceClient<org.ros.internal.message.Message, org.ros.internal.message.Message> serviceClient;

	public ProxyServiceReceiver(ProxyDispatcher proxyDispatcher, ProxyConfiguration proxyNodeConfiguration) {
		super(proxyDispatcher, proxyNodeConfiguration);
	}

	@Override
	public GraphName getDefaultNodeName() {
		return new GraphName("pros_core/proxyServiceReceiver"); //$NON-NLS-1$
	}

	@Override
	public void onStart(ConnectedNode node) {
		this.node = node;
		try {
			serviceClient = node.newServiceClient(proxyNodeConfiguration.getName(), proxyNodeConfiguration.getType());
			proxyDispatcher.registerMessageReceiver(proxyNodeConfiguration.getType(), this);
		} catch (ServiceNotFoundException e) {
			logger.debug("Can not register service client.", e); //$NON-NLS-1$
		}

	}

	@Override
	public void onMessage(Message message) {
		org.ros.internal.message.Message request = RosServiceUtils.deserializeServiceRequest(message, node,
				proxyNodeConfiguration.getType());
		UUID uuid = (UUID) message.getProperty(Props.XMPP_MSG_UUID);
		String sender = message.getFrom();
		callRosService(request, uuid, sender, node);
	}

	@Override
	public boolean isRunning() {
		return this.node != null;
	}

	@Override
	public void dispose() {
		proxyDispatcher.unregisterMessageReceiver(proxyNodeConfiguration.getType());
	}

	private void callRosService(org.ros.internal.message.Message request, final UUID uuid, final String sender,
			final Node node) {
		serviceClient.call(request, new ServiceResponseListener<org.ros.internal.message.Message>() {
			@Override
			public void onSuccess(org.ros.internal.message.Message response) {
				sendServiceResponse(response, uuid, sender, node);
			}

			@Override
			public void onFailure(RemoteException e) {
				sendServiceException(e, uuid, sender);
			}
		});
	}

	private void sendServiceResponse(org.ros.internal.message.Message response, UUID uuid, String sender, Node node) {
		Message xmppMessage = RosServiceUtils
				.serializeServiceResponse(response, node, proxyNodeConfiguration.getType());
		xmppMessage.setProperty(Props.XMPP_MSG_UUID, uuid);
		proxyDispatcher.sendXmppMessage(xmppMessage, sender);
	}

	private void sendServiceException(RemoteException e, UUID uuid, String sender) {
		Message xmppMessage = new Message();
		xmppMessage.setProperty(Props.XMPP_MSG_UUID, uuid);
		xmppMessage.setProperty(Props.XMPP_MSG_EXCEPTION, e);
		proxyDispatcher.sendXmppMessage(xmppMessage, sender);
	}

}